package org.example;

import java.util.HashSet;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutor {
	
	/* 核心线程数 */
	private final int corePoolSize;
	/* 最大线程数 */
	private final int maximumPoolSize;
	/* 存活时间 */
	private final long keepAliveTime;
	/* 任务队列 */
	private final BlockingQueue<Runnable> taskQueue;
	/* 拒绝策略 */
	private final RejectedExecutionHandler handler;
	
	/* 线程容器 */
	private final HashSet<Thread> workers = new HashSet<>();
	
	public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, int keepAliveTime, TimeUnit timeUnit,
	                          BlockingQueue<Runnable> taskQueue,
	                          RejectedExecutionHandler handler) {
		this.corePoolSize = corePoolSize;
		this.maximumPoolSize = maximumPoolSize;
		this.keepAliveTime = timeUnit.toNanos(keepAliveTime);
		this.taskQueue = taskQueue;
		this.handler = handler;
	}
	
	public void execute(Runnable command) {
		/* 线程数小于核心线程数，则创建核心线程 */
		if (workers.size() < corePoolSize) {
			addWorker(command, true);
			return;
		}
		
		/* 添加任务队列成功，则直接返回 */
		if (taskQueue.offer(command)) {
			return;
		}
		
		/* 线程数小于最大线程数，则创建非核心线程 */
		if (workers.size() < maximumPoolSize) {
			addWorker(command, false);
			return;
		}
		
		/* 拒绝策略 */
		handler.rejectedExecution(command, this);
	}
	
	private void addWorker(Runnable firstTask, boolean core) {
		Runnable coreRunnable = () -> {
			firstTask.run();
			while (true) {
				try {
					Runnable work = taskQueue.take();
					work.run();
				} catch (InterruptedException ignored) {
					return;
				}
			}
		};
		Runnable nonCoreRunnable = () -> {
			firstTask.run();
			while (true) {
				try {
					Runnable work = taskQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
					if (work != null) work.run();
					else break;
				} catch (InterruptedException ignored) {
					break;
				}
			}
			workers.remove(Thread.currentThread());
			System.out.println("线程" + Thread.currentThread().getName() + "被移除");
		};
		
		Thread thread = new Thread(core ? coreRunnable : nonCoreRunnable);
		thread.start();
		workers.add(thread);
	}
}
